/*
 * ServerStartingGameStateController.cpp
 *
 *  Created on: Dec 27, 2011
 *      Author: rudi
 */
#include <wx/filename.h>
#include <wx/socket.h>
#include "ServerStartingGameStateController.h"
#include "GameStartedGameStateController.h"
#include "LocalTurnSubStateController.h"
#include "IdleGameStateController.h"
#include "LevelUploadingGameStateController.h"
#include "ServerRegistrationHandler.h"
#include "SmartPointer.h"
#include "NetworkEventData.h"
#include "MainGameController.h"
#include "MainFrame.h"
#include "MainPanel.h"
#include "App.h"
#include "Configuration.h"

ServerStartingGameStateController::ServerStartingGameStateController(const wxString& level): mStaticDrawController(NULL, false), mIsNewLevel(false)
{
	wxChar pathSeparator = wxFileName::GetPathSeparator();
	wxString imagePath = wxGetCwd() + pathSeparator + wxGetApp().getConfiguration()->getLevels()->getPath() + pathSeparator + level + pathSeparator + level + wxT(".bmp");
	wxIPV4address address;
	int portNumber = wxGetApp().getConfiguration()->getNetworkConfiguration()->getPortNumber();
	wxSocketBase* serverSocket = wxGetApp().getServerSocket();
	IGameController* gameController = wxGetApp().getMainFrame()->getMainPanel()->getGameController();
	ServerRegistrationHandler::ServerInfo serverInfo;
	wxString response;
	bool registrationOk = false;

	mStaticDrawController.setBitmap(new wxBitmap(imagePath, wxBITMAP_TYPE_BMP));
	mStaticDrawController.refreshImage(false);
	// Start the server if needed
	if(NULL == serverSocket)
	{
		wxGetApp().getMainFrame()->GetStatusBar()->SetStatusText(wxString::Format(wxT("Starting server on port: %d"), portNumber));
		if(false == address.Service(portNumber))
		{
			wxGetApp().getMainFrame()->showMessageDialog(wxT("Failed to initialize server socket address!"), MainFrame::SERVER_ERROR);
			// Revert back to Idle state
			gameController->changeState(new IdleGameStateController());

			return;
		}
		serverSocket = new wxSocketServer(address);
		if(false == serverSocket->IsOk())
		{
			serverSocket->Destroy();
			serverSocket = NULL;
			wxGetApp().getMainFrame()->showMessageDialog(wxT("Failed to start server socket!"), MainFrame::SERVER_ERROR);
			// Revert back to Idle state
			gameController->changeState(new IdleGameStateController());

			return;
		}
		// Make the socket asynchron and set the event handler to the GameController
		serverSocket->SetEventHandler(*(static_cast<MainGameController*>(gameController)));
		serverSocket->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_OUTPUT_FLAG | wxSOCKET_CONNECTION_FLAG | wxSOCKET_LOST_FLAG);
		serverSocket->Notify(true);
		wxGetApp().setServerSocket(serverSocket);
		wxGetApp().setActualLevel(level);
		// Registering the server
		serverInfo.setPort(portNumber);
		serverInfo.setLevel(level);
		serverInfo.setFree(1);
		registrationOk = ServerRegistrationHandler::registerServer(serverInfo, response);
	}
	else
	{
		// The server is already running
		wxSocketBase* clientSocket = wxGetApp().getClientSocket();
		SmartPointer<NetworkEventData> data = NULL;

		wxGetApp().setActualLevel(level);
		if(NULL == clientSocket)
		{
			// Updating the server, since it is free again, and can accept incoming connections
			serverInfo.setPort(portNumber);
			serverInfo.setLevel(level);
			serverInfo.setFree(1);
			registrationOk = ServerRegistrationHandler::updateServer(serverInfo, response);
		}
		else
		{
			// The client remained connected, so no need to update the state - this is the NEW_LEVEL scenario
			// Just send the information to it
			mIsNewLevel = true;
			data = new NetworkEventData();
			data->setDataType(NetworkEventData::NEW_LEVEL);
			data->setStringData(level);
			if(false == data->write(clientSocket))
			{
				// Drop the connection to be able to handle an other one
				clientSocket->Destroy();
				clientSocket = NULL;
				wxGetApp().setClientSocket(NULL);
				wxGetApp().getMainFrame()->showMessageDialog(wxT("Could not send new level packet to the client!"), MainFrame::SERVER_ERROR);
				// Updating the server, since it is free again, and can accept incoming connections
				serverInfo.setPort(portNumber);
				serverInfo.setLevel(level);
				serverInfo.setFree(1);
				registrationOk = ServerRegistrationHandler::updateServer(serverInfo, response);
			}
			else
			{
				wxGetApp().getMainFrame()->getMainPanel()->Refresh();
				// We need to wait for the client's answer so just return here
				return;
			}
		}
	}
	if(false == registrationOk)
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Could not register the server: " + response), MainFrame::SERVER_ERROR);
	// Enable/disable the menus according to the state
	wxGetApp().getMainFrame()->setJoinEnabled(false);
	wxGetApp().getMainFrame()->setStartHostingEnabled(false);
	wxGetApp().getMainFrame()->setChangeLevelEnabled(false);
	wxGetApp().getMainFrame()->setKickOffClientEnabled(false);
	wxGetApp().getMainFrame()->setConfigureEnabled(false);
	wxGetApp().getMainFrame()->GetStatusBar()->SetStatusText(wxT("Server started, waiting for incoming connections"));
	wxGetApp().getMainFrame()->getMainPanel()->Refresh();
}

/*virtual*/
ServerStartingGameStateController::~ServerStartingGameStateController()
{
	// Nothing to do yet
}

//
// Methods inherited from Controller
//

void ServerStartingGameStateController::handleMouseDown(const wxPoint&)
{
	// Nothing to do
}

void ServerStartingGameStateController::handleMouseUp(const wxPoint&)
{
	// Nothing to do
}

bool ServerStartingGameStateController::handleMouseMotion(const wxPoint&)
{
	// Nothing to do
	return false;
}

const wxBitmap& ServerStartingGameStateController::getBitmapToPaint() const
{
	return mStaticDrawController.getBitmapToPaint();
}

wxBitmap& ServerStartingGameStateController::getBitmapToDraw()
{
	return mStaticDrawController.getBitmapToDraw();
}

//
// Methods inherited from GameStateController
//

void ServerStartingGameStateController::handleSocketConnection(wxSocketEvent&)
{
	SmartPointer<NetworkEventData> data = NULL;
	wxSocketBase* serverSocket = wxGetApp().getServerSocket();
	wxSocketBase* socket = wxGetApp().getClientSocket();

	wxASSERT_MSG(serverSocket != NULL, wxT("ServerStartingGameStateController::handleSocketConnection called on client side. Could not happen!"));
	if(NULL != socket)
	{
		// We are in the middle of accepting a connection. Reject this one.
		data = new NetworkEventData();
		data->rejectConnection(static_cast<wxSocketServer*>(serverSocket));

		return;
	}
	socket = static_cast<wxSocketServer*>(serverSocket)->Accept();
	if(NULL == socket || true == socket->Error())
	{
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Could not accept incoming requests!"), MainFrame::SERVER_ERROR);
		if(NULL != socket)
		{
			socket->Destroy();
			socket = NULL;
		}

		return;
	}
	wxGetApp().setClientSocket(socket);
	// Send back the actual level to the client
	data = new NetworkEventData();
	data->setDataType(NetworkEventData::CONNECTING);
	data->setStringData(wxGetApp().getActualLevel());
	if(false == data->write(socket))
	{
		// Drop the connection to be able to handle an other one
		socket->Destroy();
		socket = NULL;
		wxGetApp().setClientSocket(NULL);
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Could not send level to the client!"), MainFrame::SERVER_ERROR);

		return;
	}
	// Make the socket asynchron and set the event handler to the GameController
	socket->SetEventHandler(*(static_cast<MainGameController*>(wxGetApp().getMainFrame()->getMainPanel()->getGameController())));
	socket->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_OUTPUT_FLAG | wxSOCKET_CONNECTION_FLAG | wxSOCKET_LOST_FLAG);
	socket->Notify(true);
}

void ServerStartingGameStateController::handleSocketLost(wxSocketEvent&)
{
	wxASSERT_MSG(false, wxT("ServerStartingGameStateController::handleSocketLost() invoked. It could not happen!"));
}

void ServerStartingGameStateController::handleSocketInput(wxSocketEvent& socketEvent)
{
	wxString level = wxGetApp().getActualLevel();
	LocalTurnSubStateController* localTurnController = NULL;
	SmartPointer<NetworkEventData> data = NULL;
	wxSocketBase* clientSocket = wxGetApp().getClientSocket();
	IGameController* gameController = wxGetApp().getMainFrame()->getMainPanel()->getGameController();
	int chosenFreqency;
	ServerRegistrationHandler::ServerInfo serverInfo;
	int portNumber = wxGetApp().getConfiguration()->getNetworkConfiguration()->getPortNumber();
	wxString response;
	wxString message;

	// If the event is not on the client socket just return
	if(socketEvent.GetSocket() != clientSocket)
		return;
	wxASSERT_MSG(NULL != clientSocket, wxT("Client socket is NULL, when receiving data. Could not be!"));
	data = new NetworkEventData();
	if(false == data->read(clientSocket))
	{
		// Drop the connection if we could not read the needed data
		clientSocket->Destroy();
		clientSocket = NULL;
		wxGetApp().setClientSocket(NULL);
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Failed to receive data from the client"), MainFrame::SERVER_ERROR);

		return;
	}
	switch(data->getDataType())
	{
		case NetworkEventData::NEW_LEVEL_REJECT:
			// Could not change to the new level, drop the connection, and wait for other clients to connect
			clientSocket->Destroy();
			clientSocket = NULL;
			wxGetApp().setClientSocket(NULL);
			// Updating the server, since it is free again, and can accept incoming connections
			serverInfo.setPort(portNumber);
			serverInfo.setLevel(level);
			serverInfo.setFree(1);
			if(false == ServerRegistrationHandler::updateServer(serverInfo, response))
				wxGetApp().getMainFrame()->showMessageDialog(wxT("Could not update the server's state: " + response), MainFrame::SERVER_ERROR);
			// Inform the user
			wxGetApp().getMainFrame()->GetStatusBar()->SetStatusText(wxT("Waiting for incoming connections"));
			wxGetApp().getMainFrame()->showMessageDialog(wxT("Could not change to the new level, because the client does not support it!"), MainFrame::SERVER_ERROR);
			break;
		case NetworkEventData::NEW_LEVEL_ACK:
			// fall through
		case NetworkEventData::CONNECTING_ACK:
			this->handleConfigurationNegotioation();
			break;
		case NetworkEventData::CONFIGURATION_ACK:
			// Cache the received data
			chosenFreqency = *((int*)(data->getRawData()));
			wxGetApp().getConfiguration()->getNetworkConfiguration()->cacheFrequency(ntohl(chosenFreqency));
			// The connection is over, let's start the game itself
			localTurnController = new LocalTurnSubStateController();
			// Change the state in the GameController to Game Started state
			gameController->changeState(new GameStartedGameStateController(SmartPointer<GameStartedSubStateController>(localTurnController)));
			// Inform the user about the connected client
			if(true == mIsNewLevel)
				message = wxT("New level has been accepted by the client. The game is started!");
			else
				message = wxT("A client is connected. The game is started!");
			wxGetApp().getMainFrame()->showMessageDialog(message, MainFrame::GAME_START);
			break;

		case NetworkEventData::DOWNLOADING_START:
			if(true == wxGetApp().getConfiguration()->getNetworkConfiguration()->uploadLevels())
			{
				// We need to upload the level to the client.
				gameController->changeState(new LevelUploadingGameStateController(data->getStringData(), this));
			}
			else
			{
				// The user do not want to upload any level. Reject the download attempt, and drop the connection
				data = new NetworkEventData();
				data->setDataType(NetworkEventData::DOWNLOADING_START_REJECT);
				data->write(clientSocket); // No error handling, since we are going to drop the connection in any way
				clientSocket->Destroy();
				clientSocket = NULL;
				wxGetApp().setClientSocket(NULL);
				// Inform the user
				wxGetApp().getMainFrame()->getStatusBar()->SetStatusText(wxT("Connection rejected, because uploading levels is disabled! You can enable it in the Configure menu."));
				wxGetApp().getMainFrame()->showMessageDialog(wxT("Connection rejected, because uploading levels is disabled!"), MainFrame::SERVER_ERROR);

				return;
			}
			break;
		default:
			wxASSERT_MSG(false, wxT("Invalid message type received during server server starting!"));
			break;
	}
}

void ServerStartingGameStateController::handleSocketOutput(wxSocketEvent&)
{
	wxASSERT_MSG(false, wxT("ServerStartingGameStateController::handleSocketOutput() invoked. It could not happen!"));
}

//
// Own methods
//

void ServerStartingGameStateController::handleConfigurationNegotioation()
{
	ServerRegistrationHandler::ServerInfo serverInfo;
	SmartPointer<Configuration::CommonConfigurationPacket> configurationData = NULL;
	SmartPointer<NetworkEventData> data = NULL;
	wxSocketBase* clientSocket = wxGetApp().getClientSocket();
	wxString response;

	// Update the server state, since we are not free any more
	serverInfo.setFree(0);
	serverInfo.setPort(wxGetApp().getConfiguration()->getNetworkConfiguration()->getPortNumber());
	if(false == ServerRegistrationHandler::updateServer(serverInfo, response))
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Could not update the server's state: " + response), MainFrame::SERVER_ERROR);
	// Send the current configuration to the client
	data = new NetworkEventData();
	data->setDataType(NetworkEventData::CONFIGURATION);
	configurationData = new Configuration::CommonConfigurationPacket(wxGetApp().getConfiguration(), wxGetApp().getActualLevel());
	data->setRawData((char*)(configurationData.get()), sizeof(Configuration::CommonConfigurationPacket));
	if(false == data->write(clientSocket))
	{
		// Drop the connection to be able to handle an other one
		clientSocket->Destroy();
		clientSocket = NULL;
		wxGetApp().setClientSocket(NULL);
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Could not send configuration packet to the client!"), MainFrame::SERVER_ERROR);

		return;
	}
}
